package com.linkai.JUC._03_lock8;

import org.omg.CORBA.TIMEOUT;

import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @Description: 可重入锁 Lock 版； ReentrantLoc底层实现：AQS
 *                使用 Lock 和 Synchronize 也是同样的效果
 *                但是 Lock 加锁解锁要对应起来，否则会造成死锁
 *
 *                ReentrantLock里面有一个内部类 Sync 继承了 AbstractQueuedSynchronizer
 *                AQS:
 *                  AbstractQuenedSynchronize抽象队列同步器，简称AQS，是一种锁机制。是一种抽象的锁机制，具体的实现有ReentrantLock。
 *                  AQS维护了一个volatile修饰的共享变量state和一个线程等待队列，多线程争用资源时被阻塞的时候就会进入这个队列。
 *                  state是共享变量，其访问方式有如下3种：getState()，setState()，compareAndSetState()，
 *                  通过尝试获取共享变量 state 的结果来对线程的状态做出处理。
 *                  线程通过 CAS 去改变 state 变量的值，成功则获取锁成功，失败则进入等待队列，等待被唤醒。
 *                ReentrantLock中的实现：
 *                  （1）state初始值设置0，表示未锁定，A线程lock()时，会调用 tryAcquire() 独占锁并将 state + 1
 *                  （2）之后其他线程再想 tryAcquire 的时候就会失败，知道 A 线程 unlock()到 state=0 为止，其他线程才有机会获取锁
 *                  （3）A释放锁之前，自己也是可以重复获取此锁的（state累加），这就是可重入的概念呢。
 *
 * @Author: 林凯
 * @Date: 2021/10/26 22:52
 */
public class ReentrantLockTest02 {

    static int number = 0;

    public static void main(String[] args) {
        PhoneReentrantLock phone = new PhoneReentrantLock();

        // 一定是一个线程的 sms() 和 call() 都执行完成之后，才会执行另一个线程的 sms() 和 call()
        new Thread(()->{
            phone.sms();
        }, "A").start();

        new Thread(() -> {
            phone.sms();
        }, "B").start();

//        testLock();
        testSyn();
    }

    // 验证 Lock 锁能够保证并发安全
    public static void testLock() {
        ReentrantLock reentrantLock = new ReentrantLock();
        ArrayList<String> arrayList = new ArrayList<>();
        for (int i = 0; i < 10000; i++) {
            int temp = i;
            new Thread(()->{
                reentrantLock.lock();
                try {
                } catch (Exception e) {
                    arrayList.add(temp + "");
                    e.printStackTrace();
                } finally {
                    reentrantLock.unlock();
                }
//                arrayList.add(temp + "");
            }).start();
        }
        while (Thread.activeCount() > 2) {

        }
        System.out.println(arrayList.size());
    }

    public static void testSyn() {
        ArrayList<String> arrayList = new ArrayList<>();
        synchronized (arrayList) {
            for (int i = 0; i < 10000; i++) {
                int temp = i;
                new Thread(()->{
                    arrayList.add(temp + "");
                    try {
                        TimeUnit.MILLISECONDS.sleep(20);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }).start();
            }
            while (Thread.activeCount() > 2){

            }
            System.out.println(arrayList.size());
        }

    }

}

class PhoneReentrantLock {
    // 使用同一个 Lock 对象，加锁和解锁要对应起来。否则会造成死锁
    ReentrantLock lock = new ReentrantLock();

    public void sms() {
        lock.lock();
        lock.lock();

        try {
            System.out.println(Thread.currentThread().getName() + "sms");
            // 同时也会获得里面的锁，此时外面的锁并不会解锁，等到里面的代码执行完毕，里面的解锁之后，外面的锁才可以解锁
            call();     // 这里也有锁
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
            lock.unlock();
        }

    }

    public void  call() {
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + "call");
            // 同时也会获得里面的锁，此时外面的锁并不会解锁，等到里面的代码执行完毕，里面的解锁之后，外面的锁才可以解锁
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}


